#include "libpsacyber.h"
#include "libsecurity.h"
#include <stdlib.h>
#include <string.h>


psa_status_t ftcm_hsm_ap_generate_random(uint8_t *output, size_t output_size)
{
    if (output == NULL) {
        return PSA_ERROR_INVALID_ARGUMENT;
    }
    
    int ret = libsec_HD_RAND_bytes(output, output_size);
    return (ret == SEC_SUCCESS) ? PSA_OK : PSA_ERROR;
}

psa_status_t ftcm_hsm_ap_hash_compute(psa_algorithm_t alg,
                                      const uint8_t *input,
                                      size_t input_length,
                                      uint8_t *hash,
                                      size_t hash_size,
                                      size_t *hash_length)
{
    if (input == NULL || hash == NULL || hash_length == NULL) {
        return PSA_ERROR_INVALID_ARGUMENT;
    }
    
    E_MSHA_MODE mode;
    switch (alg) {
        case PSA_ALG_SHA_1:
            mode = MSHA_SHA1;
            break;
        case PSA_ALG_SHA_224:
            mode = MSHA_SHA224;
            break;
        case PSA_ALG_SHA_256:
            mode = MSHA_SHA256;
            break;
        case PSA_ALG_MSHA_HMAC1:
            mode = MSHA_HMAC1;
            break;
        case PSA_ALG_MSHA_HMAC224:
            mode = MSHA_HMAC224;
            break;
        case PSA_ALG_MSHA_HMAC256:
            mode = MSHA_HMAC256;
            break;
        default:
            return PSA_ERROR_NOT_SUPPORTED;
    }
    
    unsigned int len = hash_size;
    int ret = libsec_R5_hash((unsigned char*)input, input_length, mode, hash, &len);
    *hash_length = len;
    
    return (ret == SEC_SUCCESS) ? PSA_OK : PSA_ERROR;
}
size_t ftcm_hsm_ap_hash_input_length;
psa_algorithm_t  ftcm_hsm_ap_algorithm;
uint8_t ftcm_hsm_ap_hash_input[MAX_HASH_SIZE];

psa_status_t ftcm_hsm_ap_hash_setup(psa_hash_operation_t *operation,
                                    psa_algorithm_t alg)
{
    // this operetion not support in this platform
    if(operation != NULL){
        return PSA_ERROR_INVALID_ARGUMENT;
    }
    if(alg != PSA_ALG_SHA_1 && alg != PSA_ALG_SHA_224 && alg != PSA_ALG_SHA_256){
        return PSA_ERROR_NOT_SUPPORTED;
    }
    ftcm_hsm_ap_algorithm = alg;
    ftcm_hsm_ap_hash_input_length = 0;
    return PSA_OK;

}

psa_status_t ftcm_hsm_ap_hash_update(psa_hash_operation_t *operation,
                                     const uint8_t *input,
                                     size_t input_length)
{
    memcpy(ftcm_hsm_ap_hash_input +ftcm_hsm_ap_hash_input_length, input, input_length);
    ftcm_hsm_ap_hash_input_length += input_length;
    return PSA_OK;
}

psa_status_t ftcm_hsm_ap_hash_finish(psa_hash_operation_t *operation,
                                     uint8_t *hash,
                                     size_t hash_size,
                                     size_t *hash_length)
{
   return  ftcm_hsm_ap_hash_compute(ftcm_hsm_ap_algorithm, ftcm_hsm_ap_hash_input, ftcm_hsm_ap_hash_input_length, hash, hash_size, hash_length);
}

psa_status_t ftcm_hsm_ap_cipher_encrypt(mbedtls_svc_key_id_t key,
                                        psa_algorithm_t alg,
                                        const uint8_t *input,
                                        size_t input_length,
                                        uint8_t *output,
                                        size_t output_size,
                                        size_t *output_length)
{
    if (input == NULL || output == NULL || output_length == NULL) {
        return PSA_ERROR_INVALID_ARGUMENT;
    }
    
    SEC_AES_MODE mode;
    SEC_AES_TYPE enc = SEC_AES_ENCRYPT;
    
    switch (alg) {
        case PSA_ALG_ECB_NO_PADDING:
            mode = SEC_AES_ECB;
            break;
        case PSA_ALG_CBC_NO_PADDING:
            mode = SEC_AES_CBC;
            break;
        default:
            return PSA_ERROR_NOT_SUPPORTED;
    }
    
    unsigned char dummy_iv[16] = {0};
    unsigned int dummy_iv_len = (mode == SEC_AES_ECB) ? 0 : 16;
    
    unsigned int out_len = output_size;
    int ret = libsec_AES_Encrypt(key, mode, enc, (unsigned char*)input, 
                                 input_length, NULL, 0, dummy_iv, 
                                 dummy_iv_len, output, &out_len);
    
    *output_length = out_len;
    return (ret == SEC_SUCCESS) ? PSA_OK : PSA_ERROR;
}

psa_status_t ftcm_hsm_ap_cipher_decrypt(mbedtls_svc_key_id_t key,
                                        psa_algorithm_t alg,
                                        const uint8_t *input,
                                        size_t input_length,
                                        uint8_t *output,
                                        size_t output_size,
                                        size_t *output_length)
{
    if (input == NULL || output == NULL || output_length == NULL) {
        return PSA_ERROR_INVALID_ARGUMENT;
    }
    
    SEC_AES_MODE mode;
    SEC_AES_TYPE enc = SEC_AES_DECRYPT;
    
    switch (alg) {
        case PSA_ALG_ECB_NO_PADDING:
            mode = SEC_AES_ECB;
            break;
        case PSA_ALG_CBC_NO_PADDING:
            mode = SEC_AES_CBC;
            break;
        default:
            return PSA_ERROR_NOT_SUPPORTED;
    }
    
    unsigned char dummy_iv[16] = {0};
    unsigned int dummy_iv_len = (mode == SEC_AES_ECB) ? 0 : 16;
    
    unsigned int out_len = output_size;
    int ret = libsec_AES_Encrypt(key, mode, enc, (unsigned char*)input, 
                                 input_length, NULL, 0, dummy_iv, 
                                 dummy_iv_len, output, &out_len);
    
    *output_length = out_len;
    return (ret == SEC_SUCCESS) ? PSA_OK : PSA_ERROR;
}

psa_status_t ftcm_hsm_ap_aead_encrypt(mbedtls_svc_key_id_t key,
                                      psa_algorithm_t alg,
                                      const uint8_t *nonce_external,
                                      size_t nonce_length,
                                      size_t tag_length,
                                      const uint8_t *additional_data_external,
                                      size_t additional_data_length,
                                      const uint8_t *plaintext_external,
                                      size_t plaintext_length,
                                      uint8_t *ciphertext_external,
                                      size_t ciphertext_size,
                                      size_t *ciphertext_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_aead_decrypt(mbedtls_svc_key_id_t key,
                                      psa_algorithm_t alg,
                                      const uint8_t *nonce_external,
                                      size_t nonce_length,
                                      size_t tag_length,
                                      const uint8_t *additional_data_external,
                                      size_t additional_data_length,
                                      const uint8_t *ciphertext_external,
                                      size_t ciphertext_length,
                                      uint8_t *plaintext_external,
                                      size_t plaintext_size,
                                      size_t *plaintext_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_aead_encrypt_setup(
    aead_encrypt_ctx_t *ctx,
    mbedtls_svc_key_id_t key,
    psa_algorithm_t alg,
    const uint8_t *nonce_external,
    size_t nonce_length,
    size_t tag_length,
    const uint8_t *additional_data,
    size_t ad_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_aead_encrypt_update(
    aead_encrypt_ctx_t *ctx,
    const uint8_t *plaintext,
    size_t plaintext_length,
    uint8_t *ciphertext,
    size_t ciphertext_size,
    size_t *ciphertext_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_aead_encrypt_finish(
    aead_encrypt_ctx_t *ctx,
    uint8_t *ciphertext,
    size_t ciphertext_size,
    size_t *ciphertext_length,
    uint8_t *tag,
    size_t tag_size)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_aead_decrypt_setup(
    aead_decrypt_ctx_t *ctx,
    mbedtls_svc_key_id_t key,
    psa_algorithm_t alg,
    const uint8_t *nonce_external,
    size_t nonce_length,
    size_t tag_length,
    const uint8_t *additional_data,
    size_t ad_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_aead_decrypt_update(
    aead_decrypt_ctx_t *ctx,
    const uint8_t *ciphertext,
    size_t ciphertext_length,
    uint8_t *plaintext,
    size_t plaintext_size,
    size_t *plaintext_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_aead_decrypt_finish(
    aead_decrypt_ctx_t *ctx,
    const uint8_t *tag,
    size_t tag_length,
    uint8_t *plaintext,
    size_t plaintext_size,
    size_t *plaintext_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_mac_compute(mbedtls_svc_key_id_t key,
                                     psa_algorithm_t alg,
                                     const uint8_t *input,
                                     size_t input_length,
                                     uint8_t *mac,
                                     size_t mac_size,
                                     size_t *mac_length)
{
    if (input == NULL || mac == NULL || mac_length == NULL) {
        return PSA_ERROR_INVALID_ARGUMENT;
    }
    
    E_MSHA_MODE mode;
    switch (alg) {
        case PSA_ALG_CMAC:
            mode = MSHA_HMAC256;
            break;
        default:
            return PSA_ERROR_NOT_SUPPORTED;
    }
    
    unsigned char dummy_key[16] = {0};
    unsigned int data_out_len = mac_size;
    
    int ret = libsec_r5_HMAC((unsigned char*)input, input_length, 
                             dummy_key, sizeof(dummy_key),
                             mode, SEC_KEYMODE_NORMAL, 0, 
                             mac, &data_out_len);
    *mac_length = data_out_len;
    
    return (ret == SEC_SUCCESS) ? PSA_OK : PSA_ERROR;
}

psa_status_t ftcm_hsm_ap_mac_setup(
    mac_compute_ctx_t *ctx,
    mbedtls_svc_key_id_t key,
    psa_algorithm_t alg)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_mac_update(
    mac_compute_ctx_t *ctx,
    const uint8_t *input,
    size_t input_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_mac_finish(
    mac_compute_ctx_t *ctx,
    uint8_t *mac,
    size_t mac_size,
    size_t *mac_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_rsa_generate_key(const psa_key_attributes_t *attributes, mbedtls_svc_key_id_t *key)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_rsa_encrypt(mbedtls_svc_key_id_t key,
                                     psa_algorithm_t alg,
                                     const uint8_t *input,
                                     size_t input_length,
                                     const uint8_t *salt,
                                     size_t salt_length,
                                     uint8_t *output,
                                     size_t output_size,
                                     size_t *output_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_rsa_decrypt(mbedtls_svc_key_id_t key,
                                     psa_algorithm_t alg,
                                     const uint8_t *input,
                                     size_t input_length,
                                     const uint8_t *salt,
                                     size_t salt_length,
                                     uint8_t *output,
                                     size_t output_size,
                                     size_t *output_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_rsa_sign(mbedtls_svc_key_id_t key,
                                  psa_algorithm_t alg,
                                  const uint8_t *input,
                                  size_t input_length,
                                  uint8_t *signature,
                                  size_t signature_size,
                                  size_t *signature_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

psa_status_t ftcm_hsm_ap_rsa_verify(mbedtls_svc_key_id_t key,
                                    psa_algorithm_t alg,
                                    const uint8_t *input,
                                    size_t input_length,
                                    const uint8_t *signature,
                                    size_t signature_length)
{
    return PSA_ERROR_NOT_SUPPORTED;
}

int ftcm_hsm_ap_import_key(uint32_t keyid, ftcm_key_hsm *key)
{
    if (key == NULL) {
        return -1;
    }
    
    switch (key->key_type) {
        case 0:{
            if (key->key_data.symmetric.data == NULL || key->key_data.symmetric.length == 0) {
                return -1;
            }
            if (keyid < R5_USER_KEYID_MIN || keyid > R5_USER_KEYID_MAX) {
                return -1;
            }
            int ret = libsec_r5_SetUserKey(keyid, key->key_data.symmetric.data, key->key_data.symmetric.length);
            return (ret == SEC_SUCCESS) ? 0 : -1;
        }
        case 1:{
            if (key->key_data.rsa.modulus == NULL || key->key_data.rsa.modulus_length == 0) {
                return -1;
            }
            return -1;
        }
        default:
            return -1;
    }
}

int ftcm_hsm_ap_export_pubkey_byid(uint32_t keyid, ftcm_key_hsm *key)
{
    if (key == NULL) {
        return -1;
    }
    
    switch (key->key_type) {
        case 1:
        {
            RSA_pubkey pub_key;
            RSA_prikey pri_key;
            
            pub_key.modulus = NULL;
            pub_key.pub_expo = NULL;
            pri_key.modulus = NULL;
            pri_key.pub_expo = NULL;
            pri_key.pri_expo = NULL;
            
            size_t key_len = 2048;
            
            int ret = Asymmetry_RSA_keygen(key_len, pub_key, pri_key);
            if (ret != SEC_SUCCESS) {
                return -1;
            }
            
            if (pub_key.modulus != NULL && pub_key.modu_len > 0) {
                key->key_data.rsa.modulus =(uint8_t*) malloc(pub_key.modu_len);
                if (key->key_data.rsa.modulus == NULL) {
                    if (pub_key.modulus) free(pub_key.modulus);
                    if (pub_key.pub_expo) free(pub_key.pub_expo);
                    if (pri_key.modulus) free(pri_key.modulus);
                    if (pri_key.pub_expo) free(pri_key.pub_expo);
                    if (pri_key.pri_expo) free(pri_key.pri_expo);
                    return -1;
                }
                memcpy(key->key_data.rsa.modulus, pub_key.modulus, pub_key.modu_len);
                key->key_data.rsa.modulus_length = pub_key.modu_len;
            }
            
            if (pub_key.pub_expo != NULL && pub_key.pub_len > 0) {
                key->key_data.rsa.public_exponent = (uint8_t*)malloc(pub_key.pub_len);
                if (key->key_data.rsa.public_exponent == NULL) {
                    if (key->key_data.rsa.modulus) free(key->key_data.rsa.modulus);
                    if (pub_key.modulus) free(pub_key.modulus);
                    if (pub_key.pub_expo) free(pub_key.pub_expo);
                    if (pri_key.modulus) free(pri_key.modulus);
                    if (pri_key.pub_expo) free(pri_key.pub_expo);
                    if (pri_key.pri_expo) free(pri_key.pri_expo);
                    return -1;
                }
                memcpy(key->key_data.rsa.public_exponent, pub_key.pub_expo, pub_key.pub_len);
                key->key_data.rsa.public_exponent_length = pub_key.pub_len;
            }
            
            if (pub_key.modulus) free(pub_key.modulus);
            if (pub_key.pub_expo) free(pub_key.pub_expo);
            if (pri_key.modulus) free(pri_key.modulus);
            if (pri_key.pub_expo) free(pri_key.pub_expo);
            if (pri_key.pri_expo) free(pri_key.pri_expo);
            
            return 0;
        }
        default:
            return -1;
    }
}